package dmwr.repo.gae;

import java.util.ArrayList;
import java.util.List;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.ShortBlob;

import dmwr.bean.User;
import dmwr.repo.UserRepo;

public class UserRepoGae implements UserRepo {
	private static final String KIND = "User";
	private static final String PROPERTY_PASSWORD_HASH = "passwordHash";
	private static final String PROPERTY_PASSWORD_SALT = "passwordSalt";
	private static final String PROPERTY_REMEMBER_HASH = "rememberHash";
	private static final String PROPERTY_REMEMBER_SALT = "rememberSalt";
	private static final String PROPERTY_CREATED = "created";

	@Override
	public List<User> list(int offset, int limit) {
		return users(new Query(KIND).addSort(PROPERTY_CREATED,
				Query.SortDirection.ASCENDING), offset, limit);
	}

	@Override
	public User byUsername(String username) {
		Key key = KeyFactory.createKey(KIND, username);
		return byKey(key);
	}

	@Override
	public void save(User user) {
		Entity entity;
		if (user.getId() == null) {
			entity = new Entity(KIND, user.getUsername());
			entity.setProperty(PROPERTY_CREATED, System.currentTimeMillis());
		} else {
			Key key = KeyFactory.createKey(KIND, user.getUsername());
			try {
				entity = Gae.datastoreService().get(key);
			} catch (EntityNotFoundException e) {
				throw new RuntimeException(e);
			}
		}
		entity.setProperty(PROPERTY_PASSWORD_HASH,
				new ShortBlob(user.getPasswordHash()));
		entity.setProperty(PROPERTY_PASSWORD_SALT,
				new ShortBlob(user.getPasswordSalt()));
		entity.setProperty(PROPERTY_REMEMBER_HASH,
				new ShortBlob(user.getRememberHash()));
		entity.setProperty(PROPERTY_REMEMBER_SALT,
				new ShortBlob(user.getRememberSalt()));
		Gae.datastoreService().put(entity);
		if (user.getId() == null) {
			user.setId(entity.getKey().getId());
		}
	}

	@Override
	public void delete(String username) {
		Key key = KeyFactory.createKey(KIND, username);
		Gae.datastoreService().delete(key);
	}

	@Override
	public User firstCreated() {
		return user(new Query(KIND).addSort(PROPERTY_CREATED,
				Query.SortDirection.ASCENDING));
	}

	private User byKey(Key key) {
		try {
			return user(Gae.datastoreService().get(key));
		} catch (EntityNotFoundException e) {
			return null;
		}
	}

	private List<User> users(Query query, int offset, int limit) {
		List<Entity> results = Gae.datastoreService().prepare(query)
				.asList(FetchOptions.Builder.withOffset(offset).limit(limit));
		List<User> list = new ArrayList<User>(results.size());
		for (Entity result : results) {
			list.add(user(result));
		}
		return list;
	}

	private User user(Query query) {
		List<Entity> results = Gae.datastoreService().prepare(query)
				.asList(FetchOptions.Builder.withLimit(1));
		Entity result = !results.isEmpty() ? results.get(0) : null;
		return user(result);
	}

	private User user(Entity entity) {
		ShortBlob passwordHash = (ShortBlob) entity
				.getProperty(PROPERTY_PASSWORD_HASH);
		ShortBlob passwordSalt = (ShortBlob) entity
				.getProperty(PROPERTY_PASSWORD_SALT);
		ShortBlob rememberHash = (ShortBlob) entity
				.getProperty(PROPERTY_REMEMBER_HASH);
		ShortBlob rememberSalt = (ShortBlob) entity
				.getProperty(PROPERTY_REMEMBER_SALT);
		User item = entity != null ? new User(entity.getKey().getId(),
				(String) entity.getKey().getName(),
				passwordHash != null ? passwordHash.getBytes() : null,
				passwordSalt != null ? passwordSalt.getBytes() : null,
				rememberHash != null ? rememberHash.getBytes() : null,
				rememberSalt != null ? rememberSalt.getBytes() : null,
				(Long) entity.getProperty(PROPERTY_CREATED)) : null;
		return item;
	}
}
